package com.sds.prolog.services;

import com.google.appengine.repackaged.com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Provider;

import com.sds.prolog.dao.PrologDAO;
import com.sds.prolog.model.*;
import com.sds.system.daos.AbstractDAO;
import com.sds.system.helpers.RandomUtils;
import org.apache.commons.lang.StringUtils;


import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;


@Path("/prolog/reasoning")
public class ReasoningService {
    private Provider<Reasoning> reasoningProvider;
    private Logger logger = Logger.getLogger(getClass().getName());
    private PrologDAO dao;
    private Gson gson = new Gson();

    @Inject
    public ReasoningService(Provider<Reasoning> reasoningProvider, PrologDAO dao) {
        this.reasoningProvider = reasoningProvider;
        this.dao = dao;
    }

    @GET
    @Path("/question/next/all")
    @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8")
    public String getAllQuestionInPool() {
        initReasoning();
        return reasoningProvider.get().getQuestionIdPool().toString();
    }

    @GET
    @Path("/facts/all")
    @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8")
    public String getAllFact() {
        initReasoning();
        return reasoningProvider.get().getFact().toString();
    }

    @GET
    @Path("/reset")
    @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8")
    public String resetData() {
        reasoningProvider.get().getQuestionIdPool().clear();
        reasoningProvider.get().getFact().clear();
        reasoningProvider.get().setInitialized(false);
        initReasoning();
        return "Cleared";
    }

    @GET
    @Path("/laptops/all")
    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
    public String getLaptops() {
        initReasoning();
        Set<Laptop> result = Sets.newHashSet();
        for (Laptop laptop : dao.getObjectManager(Laptop.class).findAll().perform().getCollectionResult()) {
            if (Sets.intersection(reasoningProvider.get().getFact(), laptop.getRefinedAttribute()).size() > 2) {
                result.add(laptop);
            }
        }
        return gson.toJson(result);
    }

    @GET
    @Path("/question/next")
    @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8")
    public Response getNextQuestion(@Context HttpServletResponse response) throws IOException {
        initReasoning();
        String questionId = RandomUtils.randomOneOf(reasoningProvider.get().getQuestionIdPool());
        response.getWriter().write(questionId != null ? questionId : "none");
        return Response.ok().build();
    }

    @GET
    @Path("/answer/{question_id}/{answer_text}")
    public Response answerQuestion(@PathParam("question_id") String questionId, @PathParam("answer_text") String answerText) throws IOException {
        initReasoning();
        Reasoning reasoning = reasoningProvider.get();
        for (String answerT : answerText.split("----")) {
            if (!answerT.trim().isEmpty()) {
                logger.info(String.format("answered %s with %s", questionId, answerT));
                Question question = dao.getObjectManager(Question.class).findById(questionId).perform().getSingleResult();
                if (question != null) {
                    for (Answer answer : question.getAnswers()) {
                        if (StringUtils.equalsIgnoreCase(answer.getText(), answerT)) {
                            reasoning.getFact().addAll(answer.getTargets());
                            inferFact();
                            cutFact();
                            reasoning.getQuestionIdPool().remove(questionId);
                            return Response.ok().build();
                        }
                    }

                }
            }
        }
        return Response.status(404).build();
    }

    private void initReasoning() {
        if (!reasoningProvider.get().isInitialized()) {
            reasoningProvider.get().getQuestionIdPool().add("hoi_nghe_nghiep");
            reasoningProvider.get().getQuestionIdPool().add("hoi_muc_gia");
            reasoningProvider.get().getQuestionIdPool().add("hoi_giai_tri");
            reasoningProvider.get().getQuestionIdPool().add("hoi_di_chuyen");
            reasoningProvider.get().getQuestionIdPool().add("hoi_pin");
            reasoningProvider.get().getQuestionIdPool().add("hoi_kinh_nghiem");
            reasoningProvider.get().getQuestionIdPool().add("hoi_quan_tam_thuong_hieu");
            reasoningProvider.get().getQuestionIdPool().add("hoi_bao_hanh");
            reasoningProvider.get().getQuestionIdPool().add("hoi_man_hinh");
            reasoningProvider.get().getQuestionIdPool().add("hoi_thu_nhap");
            reasoningProvider.get().setInitialized(true);
        }
    }


    private void cutFact() {
        Reasoning reasoning = reasoningProvider.get();
        Set<String> facts = Sets.newHashSet();
        Set<String> questions = Sets.newHashSet();
        for (String string : reasoning.getFact()) {
            if (dao.getObjectManager(Question.class).findById(string).perform().getSingleResult() != null) {
                questions.add(string);
                logger.info(String.format("added %s to question pools", string));
            } else {
                facts.add(string);
                logger.info(String.format("added %s to facts", string));
            }
        }
        reasoning.getFact().clear();
        reasoning.getFact().addAll(facts);
        for (String question : questions) {
            if (!reasoning.getQuestionIdPool().contains(question)) {
                reasoning.getQuestionIdPool().add(question);
            }
        }
    }

    private void inferFact() {
        Reasoning reasoning = reasoningProvider.get();
        Set<String> newFacts = Sets.newHashSet();
        for (Rule rule : dao.getObjectManager(Rule.class).findAll().perform().getCollectionResult()) {
            if (validLeft(rule, reasoning)) {
                reasoning.getFact().addAll(rule.getProduces());
            }
        }
    }

    private boolean validLeft(Rule rule, Reasoning reasoning) {
        for (String id : rule.getMaterial()) {
            if (!reasoning.getFact().contains(id)) {
                return false;
            }
        }
        return true;
    }
}
